package cn.mdmbct.douyin.common.proxy;

import lombok.extern.slf4j.Slf4j;
import sun.net.www.protocol.jar.JarURLConnection;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 抖音接口Service接口加载器 获取所有的抖音api实现的service的接口
 *
 * @author mdmbct  mdmbct@outlook.com
 * @date 5/9/2021 10:14 PM
 * @modified mdmbct
 * @since 1.0
 */
@Slf4j
public class ServiceLoader {


    private final ServiceRepository serviceRepository;


    public ServiceLoader(ServiceRepository serviceRepository) {
        this.serviceRepository = serviceRepository;
    }

    /**
     * 加载指定包下的所有抖音api接口 <br>
     * 注意： <br>
     * 会读取包下面的所有接口 所以每个被读取的接口一定得是抖音api的实现接口
     *  @param interfacePackageNames 多个抖音api实现接口所在包名
     *
     */
    public void loadServiceInterfaces(String... interfacePackageNames) {

        for (String interfacePackageName : interfacePackageNames) {
            String packagePath = interfacePackageName.replace(".", "/");
            // 定义一个枚举的集合 并进行循环来处理这个目录下的things
            Enumeration<URL> dirs;

            try {
                dirs = Thread.currentThread().getContextClassLoader().getResources(packagePath);
                // 深度搜索
                while (dirs.hasMoreElements()) {
                    URL url = dirs.nextElement();
                    // 协议名称
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)) {
                        // 文件形式
                        // 获取包的物理路径
                        String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                        // 以文件形式获取包下面所有的interface
                        searchInterfacesInPackagesInFile(interfacePackageName, filePath);
                    } else if ("jar".equals(protocol)) {
                        // jar包形式
                        JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                        searchInterfacesInPackageInJar(packagePath, jarFile);
                    } else {
                        log.warn("ServiceLoader#loadServiceInterfaces " + "Search Service: " + "UnSupport protocol " + protocol);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    private void searchInterfacesInPackagesInFile(String packageName, String packagePath) {

        File packageFile = new File(packagePath);
        if (!packageFile.isDirectory() || !packageFile.exists()) {
            log.warn("ServiceLoader#searchInterfacesInPackagesByFile " + "Search Service: " + packageFile + " is not existed!");
            return;
        }

        File[] files = packageFile.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class"));

        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    searchInterfacesInPackagesInFile(packageName, file.getAbsolutePath());
                } else {
                    // class文件
                    String fileName = file.getName();
                    // 类名
                    String className = fileName.substring(0, fileName.length() - 6);
                    try {
                        Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className);
                        // 只添加接口
                        if (aClass.isInterface()) {
                            serviceRepository.addServiceInterface(aClass);
                        }
                    } catch (Throwable e) {
                        log.warn("ServiceLoader#searchInterfacesInPackagesByFile " + "Search Service In File: " + e.getCause());
                    }

                }
            }
        }
    }

    private void searchInterfacesInPackageInJar(String packagePath, JarFile jarFile) {

        // jar包里的文件
        Enumeration<JarEntry> entries = jarFile.entries();

        String interfacePrefix = packagePath + "/";

        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();

            if (name.charAt(0) == '/') {
                name = name.substring(1);
            }

            if (name.startsWith(interfacePrefix) && name.endsWith(".class") && !entry.isDirectory()) {
                // 只搜寻packPath下的class entry
                String className = name.replace("/", ".").substring(0, name.length() - 6);
                try {
                    Class<?> aClass = Class.forName(className);
                    if (aClass.isInterface()) {
                        serviceRepository.addServiceInterface(aClass);
                    }
                } catch (Throwable e) {
                    log.warn("ServiceLoader#searchInterfacesInPackagesByJar " + "Search Service In Jar: " + e.getCause());
                }

            }

        }

    }

}
